﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.IO;

using Atosenet.PluginEngine;
using Atosenet.IO;


namespace Atosenet
{
    public static class TypeManager
    {
        #region 成员

        public const string MAIN_ASSEMBLY_NAME = "Atosenet";



        #endregion


        #region 初始化

        static TypeManager()
        {
            Init();
        }

        static void Init()
        {
            Plugins = new List<PluginAttribute>();
            LoadTypesFromDefaultAssembly();
        }

        #endregion


        #region 公开函数
                
        public static object CreateInstance(string pluginName, params object[] args)
        {
            if (string.IsNullOrEmpty(pluginName))
                return null;

            try
            {
                PluginAttribute pa = Plugins.Find((p) => { return p.PluginName == pluginName; });
                return pa == null ? null : Activator.CreateInstance(pa.AssemblyName, pa.FullTypeName, false, BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public, null, args, null, null, null).Unwrap();
            }
            catch (Exception e)
            {
                InternalTrace.TraceError("根据PluginName动态创建对象失败。{0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace);
                return null;
            }        
        }

        public static object InvokeStaticMethod(object targetPlugin, string methodName, params object[] methodArgs)
        {
            if (targetPlugin == null || string.IsNullOrEmpty(methodName))
                return null;

            try
            {
                Type type = targetPlugin.GetType();
                MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod);

                foreach (MethodInfo mi in methods)
                {
                    if (mi.Name != methodName)
                        continue;

                    ParameterInfo[] pis = mi.GetParameters();

                    if (pis.Length != methodArgs.Length)
                        continue;

                    bool isMatch = true;

                    for (int i = 0; i < pis.Length; i++)
                    {
                        if (pis[i].ParameterType != methodArgs[i].GetType())
                        {
                            isMatch = false;
                            break;
                        }
                    }

                    if (isMatch)                    
                        return mi.Invoke(null, methodArgs);
                }
                return null;
            }
            catch (Exception e)
            {
                InternalTrace.TraceError("调用PluginName为{0}的动态对象的函数{1}失败。{2}{3}{4}", targetPlugin.ToString(), methodName, e.Message, Environment.NewLine, e.StackTrace);
                return null;
            }
        }

        public static object InvokeMethod(object targetPlugin, string methodName, params object[] methodArgs)
        {
            if (targetPlugin == null || string.IsNullOrEmpty(methodName))
                return null;

            try
            {
                Type type = targetPlugin.GetType();
                MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod);

                foreach (MethodInfo mi in methods)
                {
                    if (mi.Name != methodName)
                        continue;

                    ParameterInfo[] pis = mi.GetParameters();

                    if (methodArgs == null)
                    {
                        if (pis.Length != 0)
                            continue;
                    }
                    else
                    {
                        if (pis.Length != methodArgs.Length)
                            continue;
                    }

                    bool isMatch = true;

                    for (int i = 0; i < pis.Length; i++)
                    {
                        if (pis[i].ParameterType != methodArgs[i].GetType())
                        {
                            isMatch = false;
                            break;
                        }
                    }

                    if (isMatch)
                        return mi.Invoke(targetPlugin, methodArgs);
                }
                return null;
            }
            catch (Exception e)
            {
                InternalTrace.TraceError("调用PluginName为{0}的动态对象的函数{1}失败。{2}{3}{4}", targetPlugin.ToString(), methodName, e.Message, Environment.NewLine, e.StackTrace);
                return null;
            }
        }

        public static bool LoadTypesFromFile(string fileName)
        {
            if (!File.Exists(fileName))
                return false;

            try 
            {
                Assembly asm = Assembly.LoadFrom(fileName);
                return LoadTypes(asm);                
            }
            catch (Exception e)
            {
                InternalTrace.TraceError("载入外部插件时失败, {0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace);
                return false;
            }            
        }

        public static void LoadTypesFromDirectory(string dir, bool allSubDirs)
        {
            if (!Directory.Exists(dir))
                return;

            string[] files = Directory.GetFiles(dir, "*.dll", allSubDirs ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);

            foreach (string file in files)
                LoadTypesFromFile(file);
        }

        public static bool LoadTypesFromAssembly(string assemblyName)
        {
            try
            {
                return LoadTypesFromLoadedAssembly(assemblyName);
            }
            catch (Exception e)
            {
                InternalTrace.TraceError("载入内存中程序集时失败, {0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace);
                return false;
            }
        }

        public static void RemoveCustomTypes()
        {
            RemoveAll((p) => { return p.AssemblyName != MAIN_ASSEMBLY_NAME; });
        }

        public static void RemoveAll(Predicate<PluginAttribute> match)
        {
            if (match == null)
                return;

            for (int i = Plugins.Count - 1; i >= 0; i--)
            {
                if (match(Plugins[i]))
                {
                    Plugins.RemoveAt(i);
                }
            }
        }

        #endregion


        #region 静态函数

        private static bool LoadTypesFromDefaultAssembly()
        {
            try
            {
                Plugins.Clear();
                return LoadTypesFromLoadedAssembly(MAIN_ASSEMBLY_NAME);
            }
            catch (Exception e)
            {
                InternalTrace.TraceError("载入默认程序集时失败, {0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace);
                return false;
            }
        }

        private static bool LoadTypesFromLoadedAssembly(string assemblyName)
        {
            Assembly asm = Assembly.Load(assemblyName);
            return LoadTypes(asm);
        }        

        private static bool LoadTypes(Assembly asm)
        {
            if (asm == null)
                return false;

            Type[] types = asm.GetTypes();

            foreach (Type t in types)
            {
                if (!t.IsAbstract)
                {
                    object[] attrs = t.GetCustomAttributes(typeof(PluginAttribute), true);

                    if (attrs.Length > 0)
                    {
                        PluginAttribute pa = attrs[0] as PluginAttribute;

                        if (!Plugins.Exists((tPa) => { return tPa.FullTypeName == pa.FullTypeName; }))
                            Plugins.Add(pa);
                    }
                }
            }
            return true;
        }

        private static void ForEach(Action<PluginAttribute> action)
        {
            if (action == null)
                return;

            for (int i = 0; i < Plugins.Count; i++)
            {
                action(Plugins[i]);
            }
        }

        #endregion


        #region 核心函数



        #endregion


        #region 属性

        public static List<PluginAttribute> Plugins
        {
            get;
            private set;
        }


        #endregion


        #region 事件



        #endregion
    }
}
